package com.rc.saas.mini.controller.recharge;

import com.alibaba.fastjson.JSON;
import com.rc.saas.base.MinipBaseController;
import com.rc.saas.mini.client.ChargerClientRouter;
import com.rc.saas.mini.common.base.TResponse;
import com.rc.saas.mini.common.constant.SessionConstants;
import com.rc.saas.mini.common.constant.WebConstant;
import com.rc.saas.mini.common.utils.BigDecimalUtil;
import com.rc.saas.mini.common.utils.ValidationUtil;
import com.rc.saas.mini.component.UidGenerator;
import com.rc.saas.mini.minip.vo.LoginInfoVo;
import com.rc.saas.mini.model.member.MemberInfo;
import com.rc.saas.mini.model.rc.RcOperatorAccountRecord;
import com.rc.saas.mini.model.recharge.RechargeOrder;
import com.rc.saas.mini.model.recharge.RechargeOrderExample;
import com.rc.saas.mini.model.tenant.Tenant;
import com.rc.saas.mini.service.member.MemberInfoService;
import com.rc.saas.mini.service.member.MemberTicketService;
import com.rc.saas.mini.service.rc.RcOperatorAccountRecordService;
import com.rc.saas.mini.service.recharge.RechargeOrderService;
import com.rc.saas.mini.service.tenant.TenantPaymentService;
import com.rc.saas.mini.service.tenant.TenantService;
import com.rc.saas.mini.service.tenant.TenantSettingService;
import com.rc.saas.payment.wxpay.XmlBuilder;
import com.rc.saas.payment.wxpay.config.WXPay;
import com.rc.saas.payment.wxpay.config.WXPayConfig;
import com.rc.saas.payment.wxpay.config.WXPayConstants;
import com.rc.saas.payment.wxpay.config.WxPayConfigImpl;
import com.rc.saas.payment.wxpay.utils.WXPayUtil;
import com.rc.saas.payment.wxpay.vo.WxPayNotifyVo;
import com.rc.saas.payment.wxpay.vo.WxPayRetVo;
import com.rc.saas.payment.wxpay.vo.WxSignVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;


@RestController
@RequestMapping("/wxAppPay")
@Api(tags = "12.充值.APP.微信支付（需支付测试）")
public class WxAppPayController extends MinipBaseController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Value("${weChat.baseUrl}")
    private String baseUrl;

    @Autowired
    private UidGenerator uidGenerator;

    @Autowired
    private TenantPaymentService tenantPaymentService;

    @Autowired
    private MemberInfoService memberInfoService;

    @Autowired
    private RechargeOrderService rechargeOrderService;

    @Autowired
    private TenantSettingService tenantSettingService;

    @Autowired
    private MemberTicketService memberTicketService;

    @Autowired
    private ChargerClientRouter chargerClientRouter;
    @Autowired
    private TenantService tenantService;

    @Autowired
    private RcOperatorAccountRecordService rcOperatorAccountRecordService;

    /**
     * 执行微信支付预下单,生成订单号返回给前端
     *
     * @param request
     * @param totalMoney 单位为元
     * @return
     */
    @RequestMapping(value = "/wxPrePay", method = RequestMethod.POST)
    @ApiOperation(value = "执行微信支付预下单（需要登录）", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "totalMoney", paramType = "query", value = "金额(元)", required = true)})
    public TResponse wxPrePay(HttpServletRequest request, String totalMoney) {
        logger.info("controller.wxPrePay request:{}", totalMoney);

        LoginInfoVo loginInfo = readLoginInfo(request);
        if (loginInfo == null) {
            return TResponse.FAIL_LOGIN();
        }

        //TenantPayment tenantPayment = tenantPaymentService.selectByTenantCodeAndType(redisTenantConfig.getTenantCode(), WXPayConfig.PAYMENT_TYPE);
        //if (tenantPayment == null || StringUtils.isBlank(tenantPayment.getNotifyUrl1())) {
        //    return TResponse.FAIL("未配置微信支付信息");
        //}

        MemberInfo member = memberInfoService.selectByPrimaryKey(loginInfo.getMemberId());
        if (member == null) {
            return TResponse.FAIL_LOGIN();
        }
        Tenant tenant = tenantService.selectByTenantCode(member.getTenantCode());

        if (!"0".equals(member.getStatus())) {
            return TResponse.FAIL("会员已被冻结");
        }

        if (StringUtils.isBlank(member.getMobile())) {
            return TResponse.FAIL("未绑定手机号!");
        }


        if (!ValidationUtil.checkAmount(totalMoney)) {
            return TResponse.FAIL("金额不合法");
        }
        //最低充值
        //TenantSettingExample tenantSettingExample = new TenantSettingExample();
        //tenantSettingExample.createCriteria().andTenantCodeEqualTo(member.getTenantCode());
        //TenantSetting tenantSetting = tenantSettingService.selectByExample(tenantSettingExample).stream().findFirst().orElseGet(() -> null);
        //if (tenantSetting == null) {
        //    return TResponse.FAIL("未配置运营商参数信息");
        //}
        //BigDecimal memberRechargeAmountMin = new BigDecimal(1);
        //memberRechargeAmountMin = tenantSetting.getMemberRechargeAmountMin() != null ? BigDecimal.valueOf(tenantSetting.getMemberRechargeAmountMin()) : memberRechargeAmountMin;
        //if ((new BigDecimal(totalMoney).multiply(new BigDecimal(100)).compareTo(memberRechargeAmountMin)) == -1) {
        //    return TResponse.FAIL("最低充值" + memberRechargeAmountMin.divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_DOWN) + "元");
        //}

        //商户订单号
        //商户支付的订单号由商户自定义生成，微信支付要求商户订单号保持唯一性（建议根据当前系统时间加随机序列来生成订单号）。
        //重新发起一笔支付要使用原订单号，避免重复支付；已支付过或已调用关单、撤销（请见后文的API列表）的订单号不能重新发起支付。

        String body = "订单支付";
        String nonce_str = WXPayUtil.generateNonceStr();
        String spbill_create_ip = request.getRemoteAddr();

        //交易类型
        //JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付，统一下单接口trade_type的传参可参考这里
        //MICROPAY--刷卡支付，刷卡支付有单独的支付接口，不调用统一下单接口
        String trade_type = "APP";// 此处指定为APP支付


        //生成充值订单
        RechargeOrder rechargeOrder = new RechargeOrder();
        //充值方式  0 公众号 1 APP  2小程序 默认0
        rechargeOrder.setRechargeType(WebConstant.CON1);
        //支付方式 0:微信支付 1:支付宝
        rechargeOrder.setPaymentType(WebConstant.CON0);
        rechargeOrder.setAmount(new BigDecimal(totalMoney));
        rechargeOrder.setFactAmount(new BigDecimal(totalMoney));
        rechargeOrder.setMemberId(member.getMemberId());
        rechargeOrder.setStatus(WebConstant.CON0);
        rechargeOrder.setCreatedTime(new Date());
        rechargeOrder.setTenantCode(member.getTenantCode());
        //是否符合支付减免
        //BigDecimal factAmount = rechargeOrder.getFactAmount();
        //MarketActivityTicket marketActivityTicket = memberTicketService.selectCurrentReductionActivity(member, rechargeOrder);
        //if (marketActivityTicket != null) {
        //    factAmount = factAmount.subtract(new BigDecimal(marketActivityTicket.getSendAmount()).setScale(2, BigDecimal.ROUND_HALF_UP));
        //    if (factAmount.compareTo(BigDecimal.ZERO) == 1) {
        //        logger.info("营销活动==》充值减免:充值金额{}，到账金额{}，减免金额{}", factAmount, rechargeOrder.getAmount(), marketActivityTicket.getSendAmount());
        //        rechargeOrder.setFactAmount(factAmount);
        //    }
        //}


        //订单总金额，单位为分
        //交易金额默认为人民币交易，接口中参数支付金额单位为【分】，参数值不能带小数。对账单中的交易金额单位为【元】。
        //外币交易的支付金额精确到币种的最小单位，参数值不能带小数点。
        BigDecimal targetAmount = rechargeOrder.getFactAmount();
        targetAmount = targetAmount.multiply(new BigDecimal("100"));
        String total_fee = String.valueOf(targetAmount.longValue());

        //根据不同的运营商简码生成充值订单号
        String outTradeNo = uidGenerator.genWxRechargeOutTradeNo(member.getTenantCode(), member.getMobile());

        HashMap<String, String> data = new HashMap<String, String>();
        data.put("body", body);
        data.put("nonce_str", nonce_str);
        data.put("notify_url", baseUrl + "wxAppPay/open/wxPayNotify");
        data.put("out_trade_no", outTradeNo);
        data.put("spbill_create_ip", spbill_create_ip);
        data.put("fee_type", "CNY");
        data.put("total_fee", total_fee);
        data.put("trade_type", trade_type);

        //附加数据/自定义参数,异步通知时可以原样返回
        data.put("attach", String.valueOf(member.getMemberId()));

        try {
            WxPayConfigImpl config = new WxPayConfigImpl(tenant);
            WXPay wxpay = new WXPay(config);

            Map<String, String> resp = wxpay.unifiedOrder(data);
            logger.info("微信返回预付单信息:{}", resp);

            //返回客户端支付信息
            String signInfo = JSON.toJSONString(resp);
            WxSignVo wxSignVo = JSON.parseObject(signInfo, WxSignVo.class);

            //return_code 和result_code都为SUCCESS的时候
            if (wxSignVo.getReturn_code().equals(WXPayConstants.SUCCESS) && wxSignVo.getResult_code().equals(WXPayConstants.SUCCESS)) {
                rechargeOrder.setOutTradeNo(outTradeNo);
                rechargeOrderService.insertSelective(rechargeOrder);
                RcOperatorAccountRecord rcOperatorAccountRecord = new RcOperatorAccountRecord();
                rcOperatorAccountRecord.setRecordType("0");
                rcOperatorAccountRecord.setExplains("充值");
                rcOperatorAccountRecord.setAmount(new BigDecimal(totalMoney));
                rcOperatorAccountRecord.setRecordSource("1");
                rcOperatorAccountRecord.setEntryId(member.getMemberId());
                rcOperatorAccountRecord.setEntryName(member.getNickname());
                rcOperatorAccountRecord.setEntryType("1");
                rcOperatorAccountRecord.setEntryAccount(new BigDecimal(totalMoney));
                rcOperatorAccountRecord.setOutId(1L);
                rcOperatorAccountRecord.setOutName("平台");
                rcOperatorAccountRecord.setOutType("0");
                rcOperatorAccountRecord.setStatus("1");
                rcOperatorAccountRecord.setCreatedTime(new Date());
                rcOperatorAccountRecord.setLastUpdatedTime(new Date());
                rcOperatorAccountRecord.setRemark("充值");
                rcOperatorAccountRecordService.insertSelective(rcOperatorAccountRecord);

                logger.info("微信支付预下单成功:{}", outTradeNo);

                Map<String, String> map = generatePaySign(wxpay, wxSignVo);
                TResponse tResponse = TResponse.SUCCESS();
                tResponse.put(map);
                return tResponse;
            } else {
                return TResponse.FAIL("预下单失败");
            }

        } catch (Exception e) {
            logger.error("微信支付预下单错误", e);
            return TResponse.FAIL("微信支付预下单失败");
        }
    }

    /**
     * https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_7&index=3
     * SUCCESS/FAIL,SUCCESS表示商户接收通知成功并校验成功
     * 对后台通知交互时，如果微信收到商户的应答不是成功或超时，微信认为通知失败，
     * 微信会通过一定的策略定期重新发起通知，尽可能提高通知的成功率，但微信不保证通知最终能成功。 （通知频率为15/15/30/180/1800/1800/1800/1800/3600，单位：秒）
     *
     * @param request
     * @param response
     */
    @RequestMapping(value = "/open/wxPayNotify", method = RequestMethod.POST)
    @ApiIgnore
    public synchronized String wxPayNotify(HttpServletRequest request, HttpServletResponse response) {
        HttpSession session = request.getSession();
        String tenantCode = (String) request.getSession().getAttribute(SessionConstants.SESSION_TENANT);

        Tenant tenant = tenantService.selectByTenantCode(tenantCode);
        String url = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
                + request.getServletPath();

        logger.info("wxPayNotify异步通知:{}", url);

        String reqStr = readReqStr(request);

        if (isnull(reqStr)) {
            return voToXml(new WxPayRetVo(WXPayConstants.FAIL, "微信异步通知报文为空"));
        }

        logger.info("接收微信支付异步通知数据:{}", reqStr);
        Map<String, String> respData = new HashMap<String, String>();

        WxPayConfigImpl config = new WxPayConfigImpl(tenant);
        try {
            respData = WXPayUtil.xmlToMap(reqStr);

            // 返回数据的签名方式和请求中给定的签名方式是一致的
            if (!WXPayUtil.isSignatureValid(respData, config.getKey(), WXPayConstants.SignType.MD5)) {
                logger.error("微信支付异步通知验签失败");
                return voToXml(new WxPayRetVo(WXPayConstants.FAIL, "参数格式校验错误"));
            }
        } catch (Exception e) {
            logger.error("微信异步通知报文解析异常", e);
            return voToXml(new WxPayRetVo(WXPayConstants.FAIL, "参数格式校验错误"));
        }

        if (respData.isEmpty()) {
            logger.error("wxPayNotify异步通知报文为空");
            return voToXml(new WxPayRetVo(WXPayConstants.FAIL, "报文为空"));
        }

        //返回客户端支付信息
        String signInfo = JSON.toJSONString(respData);
        WxPayNotifyVo wxNotifyVo = JSON.parseObject(signInfo, WxPayNotifyVo.class);
        wxNotifyVo.setNotify_url(url);

        //3、校验通知中的seller_id（或者seller_email) 是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），
        //4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过，则表明本次通知是异常通知，务必忽略
        if (!wxNotifyVo.getMch_id().equals(config.getMchID()) || !wxNotifyVo.getAppid().equals(config.getAppID())) {
            logger.error("wxNotify:mch_id或appid有误:{},{}", wxNotifyVo.getMch_id(), wxNotifyVo.getAppid());
            return voToXml(new WxPayRetVo(WXPayConstants.FAIL, "wxNotify:mch_id或appid有误"));
        }

        //判断result_code标记
        if (!wxNotifyVo.getResult_code().trim().equals(WXPayConstants.SUCCESS)) {
            logger.error("result_code标记不为SUCCESS:{}", wxNotifyVo.getResult_code());
            return voToXml(new WxPayRetVo(WXPayConstants.FAIL, "result_code标记不为SUCCESS"));
        }

        //入账处理
        RechargeOrderExample example = new RechargeOrderExample();
        example.createCriteria().andOutTradeNoEqualTo(wxNotifyVo.getOut_trade_no());
        RechargeOrder rechargeOrder = rechargeOrderService.selectByExample(example).stream().findFirst().orElseGet(() -> null);//获取微信充值订单
        if (!"1".equals(rechargeOrder.getStatus())) {//支付状态 未支付
            //memberID验证
            if (!wxNotifyVo.getAttach().equals(String.valueOf(rechargeOrder.getMemberId()))) {
                logger.error("入帐会员ID不一致:{},:{}", wxNotifyVo.getOut_trade_no(), wxNotifyVo.getAttach() + ":" + rechargeOrder.getMemberId());
                return voToXml(new WxPayRetVo(WXPayConstants.FAIL, "入帐会员ID不一致"));
            }

            //订单总金额：将单位分转为元
            Double newTotalFee = BigDecimalUtil.divide(Double.valueOf(wxNotifyVo.getTotal_fee()), Double.valueOf("100"));
            newTotalFee = BigDecimalUtil.round(newTotalFee, 2);
            String newTotalFeeStr = new DecimalFormat("0.00").format(newTotalFee);

            //微信流水号
            String transactionId = wxNotifyVo.getTransaction_id();
            //金额验证
            if (!String.valueOf(rechargeOrder.getFactAmount()).equals(newTotalFeeStr)) {
                logger.error("入帐金额不一致,:{},:{}", wxNotifyVo.getOut_trade_no(), rechargeOrder.getFactAmount() + ":" + newTotalFeeStr);
                return voToXml(new WxPayRetVo(WXPayConstants.FAIL, "入帐金额不一致"));
            }

            Long memberId = null;
            try {
                //实际支付
                //Double cashFee = BigDecimalUtil.divide(Double.valueOf(wxNotifyVo.getCash_fee()), Double.valueOf("100"));

                memberId = rechargeOrder.getMemberId();
                //payType:交易方式(0:微信,1:支付宝)
//                rechargeOrder = rechargeOrderService.updateRechargeOrderFinish(rechargeOrder.getRechargeOrderId(), rechargeOrder.getPaymentType(), transactionId);
               boolean flag= rechargeOrderService.updateRechargeOrder(rechargeOrder.getRechargeOrderId(), "0", transactionId);
                if (flag) {
                    logger.info("****微信支付回调处理成功！****:{},:{},:{}", wxNotifyVo.getOut_trade_no(), rechargeOrder.getAmount(), rechargeOrder.getFactAmount());
                    return voToXml(new WxPayRetVo(WXPayConstants.SUCCESS, "OK"));
                }else {
                    return voToXml(new WxPayRetVo(WXPayConstants.FAIL, "微信支付入账失败"));
                }

            } catch (Exception e) {
                return voToXml(new WxPayRetVo(WXPayConstants.FAIL, "微信支付入账失败"));
            } finally {
                //最后下发余额更新指令
                if (memberId != null) {
                    chargerClientRouter.sendUserAccount(memberId, null);
                }
            }

        } else {
            //如果已入帐
            logger.info("微信支付回调已入帐:{},:{}", wxNotifyVo.getOut_trade_no(), rechargeOrder.getAmount());
            return voToXml(new WxPayRetVo(WXPayConstants.SUCCESS, "OK"));
        }
    }

    /**
     * 生成APP端调起支付的参数列表签名map
     * https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_12
     *
     * @return
     */
    private Map<String, String> generatePaySign(WXPay wxpay, WxSignVo wxSignVo) {
        WXPayConfig config = wxpay.getConfig();
        HashMap<String, String> data = new HashMap<String, String>();
        data.put("appid", wxSignVo.getAppid());
        data.put("partnerid", wxSignVo.getMch_id());

        data.put("prepayid", wxSignVo.getPrepay_id());
        data.put("package", "Sign=WXPay");
        data.put("noncestr", wxSignVo.getNonce_str());
        data.put("timestamp", WXPayUtil.generateTimestamp());

        try {
            String sign = WXPayUtil.generateSignature(data, config.getKey(), wxpay.getSignType());
            data.put("sign", sign);

            return data;
        } catch (Exception e) {
            logger.error("微信支付预下单签名错误", e);
            return null;
        }
    }

    /**
     * WxRetVo转XML
     *
     * @param wxRetVo
     * @return
     */
    private String voToXml(WxPayRetVo wxRetVo) {
        //将java对象转换为XML字符串
        return XmlBuilder.convertToXml(wxRetVo);
    }

    /**
     * 读取request流
     *
     * @param request
     * @return
     */
    public static String readReqStr(HttpServletRequest request) {
        BufferedReader reader = null;
        StringBuilder sb = new StringBuilder();
        try {
            reader = new BufferedReader(new InputStreamReader(request.getInputStream(), "utf-8"));
            String line = null;

            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != reader) {
                    reader.close();
                }
            } catch (IOException e) {

            }
        }
        return sb.toString();
    }

    /**
     * str空判断
     *
     * @param str
     * @return
     * @author guoyx
     */
    public boolean isnull(String str) {
        if (null == str || str.equalsIgnoreCase("null") || str.equals("")) {
            return true;
        } else {
            return false;
        }
    }
}